﻿/**
 * FFXIIRCGateway
 *
 * @package ffxiircgateway
 * @version $Id: ProcessWatcher.cs 22 2009-05-10 02:17:33Z ayunyan $
 * @author ayunyan <ayu@commun.jp>
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link http://code.google.com/p/ffxiircgateway
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace FFXIIRCGateway
{
    public class ProcessWatcher
    {
        public delegate void ProcessStartedEventHandler(Process hProcess);
        public event ProcessStartedEventHandler ProcessStarted;
        public delegate void ProcessExitedEventHandler();
        public event ProcessExitedEventHandler ProcessExited;
        public delegate void ModuleLoadedEventHandler(Process hProcess, ProcessModule hModule);
        public event ModuleLoadedEventHandler ModuleLoaded;
        public delegate void ModuleUnloadedEventHandler();
        public event ModuleUnloadedEventHandler ModuleUnloaded;

        private volatile string processName;
        private volatile string moduleName;
        private volatile Process hProcess = null;
        private volatile ProcessModule hModule = null;
        private System.Threading.Timer timer;

        public ProcessWatcher(string ProcessName, string ModuleName)
        {
            this.processName = ProcessName;
            this.moduleName = ModuleName;
        }

        public void Start()
        {
            this.timer = new System.Threading.Timer(new TimerCallback(this.timerCallack), null, 0, 500);
        }

        public void Stop()
        {
            this.timer.Change(-1, -1);
        }

        private void timerCallack(object e)
        {
            if (this.hProcess == null)
            {
                this.hProcess = this.findProcess(this.processName);
                if (this.hProcess == null) return;
                this.hProcess.EnableRaisingEvents = true;
                this.hProcess.Exited += new EventHandler(processExited);
                this.OnProcessStarted(this.hProcess);
            }
            else
            {
                this.hProcess.Refresh();
            }

            if (this.hModule == null)
            {
                this.hModule = this.findModule(this.moduleName);
                if (this.hModule == null) return;
                this.OnModuleLoaded(this.hProcess, this.hModule);
            }
            else
            {
                this.hModule = this.findModule(this.moduleName);
                if (this.hModule != null) return;
                this.OnModuleUnloaded();
            }
        }

        void processExited(object sender, EventArgs e)
        {
            if (this.hModule != null) this.OnModuleUnloaded();
            this.OnProcessExited();
            this.hModule = null;
            this.hProcess.Close();
            this.hProcess = null;
        }

        private Process findProcess(string ProcessName)
        {
            Process[] processes = Process.GetProcessesByName(ProcessName);
            if (processes.Length < 1) return null;
            return processes[0];
        }

        private ProcessModule findModule(string ModuleName)
        {
            try
            {
                for (int i = 0; i < this.hProcess.Modules.Count; i++)
                {
                    if (this.hProcess.Modules[i].ModuleName.ToLower() == ModuleName.ToLower())
                    {
                        return this.hProcess.Modules[i];
                    }
                }
            }
            catch
            {
            }
            return null;
        }

        private void OnProcessStarted(Process hProcess)
        {
            if (this.ProcessStarted != null)
            {
                ProcessStarted(hProcess);
            }
        }

        private void OnProcessExited()
        {
            if (this.ProcessExited != null)
            {
                ProcessExited();
            }
        }

        private void OnModuleLoaded(Process hProcess, ProcessModule hModule)
        {
            if (this.ModuleLoaded != null)
            {
                ModuleLoaded(hProcess, hModule);
            }
        }

        private void OnModuleUnloaded()
        {
            if (this.ModuleUnloaded != null)
            {
                ModuleUnloaded();
            }
        }
    }
}
